# -*- coding: utf-8 -*-
# @Author  : LvShenkai
# @Time    : 2021/9/14 15:47
# @Function: DCP数据进行聚类
import datetime
import pandas
import numpy as np
from cluster.cluster_increment import Cluster_tool_increment as Cluster_tool
from dataload.dataset import DataSet
from dataload.oracle_link import get_data_from_database, classify_train_and_test
from parameter.parameter import Parameter
import matplotlib.pyplot as plt


class Entrance:
    """
        增量预测入口

    """
    @staticmethod
    def distance_cal(center: list, target: list):
        """
            计算两个向量的欧氏距离
        :param center: 聚类中心向量,只包含需要比较的字段，如比到TTL13，则0-11都不需要放入，只保存90-13
        :param target: 目标向量，只包含需要比较的字段
        :return: 两个向量之间的欧氏距离
        """
        distance = 0.0  # 距离
        assert len(center) == len(target)  # 两个向量长度必须相同
        for index in range(len(center)):
            distance += (center[index] - target[index]) ** 2
        return distance

    @staticmethod
    def predict(centers: list, index: int, target: list):
        """
            找出目标数据最近的聚类中心
        :param centers: 所有的聚类中心，字典形式给出
        :param index: 聚类索引
        :param target: 目标数据，只包含需要聚类的数据列，其余的不要
        :return: 返回对应的聚类标签，即是哪一类
        """
        df_centers_complete = DataSet.data_load(centers, phase='center')  # 转换为df
        df_centers_complete = df_centers_complete[df_centers_complete['index'] == index]  # 筛选出对应索引的聚类中心
        # 只保留需要的列
        need_columns = ['cluster_label']
        need_columns.extend(Parameter.COLUMNS_DICT_CENTER.value[index])
        df_centers = df_centers_complete[need_columns]

        min_center = 0  # 最接近的聚类中心
        min_distance = float('inf')  # 最小距离
        # 按行遍历df_centers，找出距离最小的聚类中心
        for index, row in df_centers.iterrows():
            center_list = list(row)  # 当前聚类中心
            dictance = Entrance.distance_cal(center_list[1:], target)  # 计算距离
            if dictance <= min_distance:
                min_center = center_list[0]
                min_distance = dictance

        # 对应的聚类中心类别的ITTLN1均值为预测值,这个预测值是增量
        a = df_centers_complete[df_centers_complete['cluster_label'] == min_center]['DN1_mean']
        # 增量+当前TTL值为真实预测值
        predict_ITTLN1 = a.iloc[0] + target[-1]
        return int(min_center), predict_ITTLN1

    @staticmethod
    def test(centers: list, datas_test: list):
        """
            对测试机数据进行测试
        :param centers: 聚类中心
        :param datas_test: 测试集数据
        :return: 返回对测试集的预测结果
        """
        df_datas_test = DataSet.data_load(datas_test, phase='test')  # 转为DataFrame格式
        df_datas_test.reset_index(drop=True)

        # 用于保存预测结果
        result_test_predict = pandas.DataFrame(columns=Parameter.COLUMNS_PREDICT_RESULT.value)
        FLIGHTDATE = df_datas_test['FLIGHTDATE'].tolist()
        ITTLN1 = df_datas_test['ITTLN1'].tolist()
        result_test_predict['FLIGHTDATE'] = FLIGHTDATE
        result_test_predict['ITTLN1'] = ITTLN1
        df_datas_test.reset_index(drop=True, inplace=True)

        # 遍历所有数据
        for i, row in df_datas_test.iterrows():
            # print('正在预测第', i, '个数据')
            # 遍历所有index
            for index in Parameter.INDEXS.value:
                target = list(row[Parameter.COLUMNS_DICT_CENTER.value[index]])
                cluster_label, predict_ITTLN1 = Entrance.predict(centers, index, target)
                column = 'C' + str(index)
                result_test_predict.at[i, column] = round(predict_ITTLN1)

        return result_test_predict

    @staticmethod
    def cluster_entrance(data_train: list, plt_title):
        """
            聚类入口，传入训练数据，返回聚类中心
        :param data_train: 训练数据，格式为由dict组成的list，例:[{'ITTL360': 1, 'ITTL180': 3},{'ITTL360': 2, 'ITTL180': 5}]
        :param plt_title : 图标题，即航班号
        :return: 返回聚类中心，格式为由dict组成的list，例:
                [{'index':180,'cluster_label':0,'ITTL360': 1, 'ITTL180': 3},
                {'index':180,'cluster_label':1,'ITTL360': 2, 'ITTL180': 5}]
                以及聚类中心的dataFrame格式
        """

        # 加载数据
        data = DataSet.data_load(data_train)

        # 构造聚类工具实例
        cluster_tool = Cluster_tool(data)

        # 所有的聚类中心
        cluster_centers = pandas.DataFrame(columns=Parameter.COLUMNS_CENTER.value)

        # 依次聚类,通过肘部拐点计算确定聚类中心个数
        for end in Parameter.END.value:
            # 获得最佳的聚类中心个数
            best_num_center, elbow_result = cluster_tool.elbow_rule(max_num_center=30,
                                                                    start=Parameter.START.value,
                                                                    end=end)

            # 画出拐点图
            elbow_result = np.array(elbow_result)
            num_center = np.array([i for i in range(1, len(elbow_result) + 1)])
            # 设置中文编码问题
            plt.rcParams['font.sans-serif'] = ['SimHei']
            plt.rcParams['axes.unicode_minus'] = False
            # 画图
            plt.plot(num_center, elbow_result, lw=2)
            # f = plt.gcf()
            # f.savefig(r'../拐点图/{}.png'.format(end))
            # 设置背景网格
            plt.grid(linestyle='-.')
            # 设置轴名称
            plt.ylabel('畸变值', fontsize=10)
            plt.xlabel('聚类中心数', fontsize=10)
            # 设置标题
            plt.title(plt_title)

            plt.show()

            # 进行聚类
            df_data, df_cluster_center = cluster_tool.k_means(start=Parameter.START.value, end=end,
                                                              num_center=best_num_center)
            # 记录聚类中心
            cluster_centers = pandas.concat([cluster_centers, df_cluster_center])

        # 指定聚类中心个数
        for end in Parameter.END.value:
            best_num_center = Parameter.FIXED_NUM_CENTER.value
            # 进行聚类
            df_data, df_cluster_center = cluster_tool.k_means(start=Parameter.START.value, end=end,
                                                              num_center=best_num_center)
            # 记录聚类中心
            cluster_centers = pandas.concat([cluster_centers, df_cluster_center])
        centers_dic_list = DataSet.df_2_dic_list(cluster_centers)
        return centers_dic_list, cluster_centers


if __name__ == '__main__':
    # for flight_no in ['8501 ', '8052 ', '8064 ', '8067 ', '8068 ', '8207 ', '8208 ', '8217 ', '8218 ',
    #                   '8233 ', '8234 ', '8247 ', '8248 ', '8323 ', '8324 ', '8325 ', '8326 ', '8329 ',
    #                   '8330 ', '8347 ', '8348 ', '8412 ', '8445 ', '8446 ', '8483 ', '8484 ', '8485 ',
    #                   '8486 '
    #                   ]:
    for flight_no in ['8616', '8501']:
        print(flight_no)
        # 从数据库中得到数据
        data_list_dic = get_data_from_database(flight_no)
        # 数据分为训练数据和测试数据
        data_train, data_test = classify_train_and_test(data_list_dic)
        # 进行聚类，得到矩阵中心的dict格式和dataFrame格式
        centers, cluster_centers_df = Entrance.cluster_entrance(data_train, flight_no)
        # print('#' * 100)
        # print('\033[33mcenter\033[0m')
        # for i in centers:
        #     print(i)

        # 预测
        df_data_test = Entrance.test(centers, data_test)
        # print(df_data_test)

        rmses = [0, 0]
        num_test = df_data_test.shape[0]  # 测试集行数
        TTLN1 = np.array(df_data_test['ITTLN1'].tolist())
        column_test = list(df_data_test)
        for i in range(2, len(column_test)):
            cur_TTL = np.array(df_data_test[column_test[i]].tolist())
            cur_rmse = np.sqrt(((cur_TTL - TTLN1) ** 2).sum() / num_test)
            rmses.append(cur_rmse)
        print(rmses)
